<?php
namespace bee;

abstract class DAO
{
  /**
   *
   * @var db
   */
  protected $__conn;
  /**
   *
   * @var \Exception
   */
  //public    $__exception;
  protected $__table;
  protected $__insert_array;
  protected $__update_array;
  protected $__param_types;
  protected $__PKAutoNumber = true;
  protected $__PKname = "id";
  protected  $__SQL = array();

  const POST_NONE         = 0;
  const POST_SELECT       = 1;
  const PK_AUTO           = 256;





  private function namify($name)
  {
    return $this->__conn->__NAME_OPEN . $this->__table . $this->__conn->__NAME_CLOSE;
  }

  public function SELECT($id = NULL)
  {
    $pkName = $this->__PKname;
    if($id === NULL)
  	{	$id = $this->$pkName;
  	}

  	if(!$this->__SQL["SELECT"])
  	{
  	  $c = $this->__conn;
  	  $this->__SQL["SELECT"] = "SELECT * FROM " . $this->namify($this->__table) . " WHERE " . $this->namify($pkName) . " = :$pkName";
  	  $stmt = $c->prepare($this->__SQL["SELECT"]);
  	}

  	$stmt->bindParam(":$pkName", $id);
  	$stmt->setFetchMode(\PDO::FETCH_INTO, $this);
  	if($stmt->execute())
  	{
  	  if(!$stmt->fetch(\PDO::FETCH_INTO))
  	  {
  	    $this->clear();
  	  }
  	  return $this;
  	}
  	throw new exception("Statement " . $this->__SQL["SELECT"] . " execution error" , 6969);
  }

  public function DELETE()
  {
    $pkName = $this->__PKname;
    if($this->__SQL["DELETE"])
    {
      $this->__SQL["DELETE"] = "DELETE FROM " . $this->namify($this->__table). " WHERE " .$this->namify($pkName) . " = :$pkName";
      $stmt = $this->__conn->prepare($this->__SQL["DELETE"]);
    }
  	$stmt->bindParam(":$pkName", $this->$pkName, \PDO::PARAM_INT);
  	if($stmt->execute())
  	{
  	  return $this;
  	}
  	throw new exception("Statement " . $this->__SQL["DELETE"] . " execution error" , 6969);
  }

	/**
	 * @return int
	 */
  public function INSERT($params = DAO_POST_SELECT)
  {
    if(!$this->__SQL["INSERT"])
    {
      $c = $this->__conn;
      $this->__SQL["INSERT"] = "INSERT INTO " . $this->namify($this->__table) . " (" . $c->__NAME_OPEN
  	   . implode($c->__NAME_CLOSE . ", " . $c->__NAME_OPEN, $this->__insert_array) . $c->__NAME_CLOSE . ") VALUES (:"
  	   . implode(", :", $this->__insert_array) . ")";
  		$stmt = $this->__conn->prepare($this->__SQL["INSERT"]);
    }
		foreach ($this->__insert_array as $insert_field)
		{ if($insert_field != $this->__PKname)
		  {
			  $stmt->bindParam(":" . $insert_field, $this->$insert_field); // doraditi bindanje po tipu
		  }
		}
		/**
		 * @todo problem kada pk nije autonumber
		 */
		if($stmt->execute())
		{ if( ($params & DAO_POST_SELECT) == DAO_POST_SELECT)
		  { if(($c->getConnectionParams()->DB_ENGINE & DB_ENGINE_MSSQL) == DB_ENGINE_MSSQL && false)
  		  { $stmt = $this->__conn->query("SELECT @@IDENTITY");
          $row = $stmt->fetch(\PDO::FETCH_NUM);
          $this->SELECT($row[0]);
        } else
        { $this->SELECT($this->__conn->lastInsertId());
        }
		  }
		  return $this;
		}

		throw new exception("Statement " . $this->__SQL["INSERT"] . " execution error" , 6969);
  }

	public function UPDATE()
	{ $PKname = $this->__PKname;

	  if(!$this->__SQL["UPDATE"])
	  {
	    $this->__SQL["UPDATE"] = "UPDATE " . $this->namify($this->__table)
	     . " SET " . implode(", ", $this->__update_array) . " WHERE " . $this->namify($PKname ) . " = :$PKname ";
	    $stmt = $this->__conn->prepare($this->__SQL["UPDATE"]);
	  }

	  foreach ($this->__insert_array as $insert_field)
	  {
	    $stmt->bindParam(":" . $insert_field, $this->$insert_field);//, $this->param_types[$insert_field]); // doraditi bindanje po tipu
    }
    $stmt->bindParam(":$PKname", $this->$PKname, \PDO::PARAM_INT);

    if($stmt->execute())
    { return $this;
    }
    throw new exception("Statement" . $this->__SQL["UPDATE"] . " execution error", 6969);
	}

	public function __construct($pk = 0, db $connection = NULL)
	{
	  if($connection)
	  { $this->__conn = $connection;
	  } else
	  { $this->__conn = db::getInstance();
	  }

	  if($pk)
	  {	$this->SELECT($pk);
	  }
    $this->__table = str_replace("\\", "_", get_called_class());

      $where_refl = new \ReflectionClass(get_called_class());
      $cls_props = $where_refl->getProperties(ReflectionProperty::IS_PUBLIC);
      $this->__insert_array = array();
      $this->__update_array = array();

      foreach ($cls_props as $prop)
      {	if(!in_array($prop->name, $this->DAO_props) && ($prop->name != $this->__PKname || $this->__PKAutoNumber == false))
      	{ $this->__update_array[$prop->name] = $this->namify($prop->name) . " = :" . $prop->name;// . $daoRow->prepareSQL($daoRow->{$prop->name}, true);
      	  $this->__insert_array[":" . $prop->name] = $prop->name;
      	}
      }
	}


	public function getTableName()
	{	return $this->__table;
	}

	public function getConn()
	{	return $this->__conn;
	}

	public function fillFromArray($array = "", $arr_prefix = "", $empty2null = true)
	{
	  if(!is_array($array))
		{
		  $array = $_REQUEST;
		}

		$refl = new \ReflectionClass(get_class($this));
		$class_props = $refl->getProperties(ReflectionProperty::IS_PUBLIC);
		foreach ($class_props as $prop)
		{
		  if(isset($array[$arr_prefix . $prop->name]))// && !in_array($prop->name, $this->DAO_props))
			{
			  $pn = $prop->name;
				$this->$pn = $array[$arr_prefix . $pn];
			}
		}
	}

	public function clear()
	{
	  $refl = new \ReflectionClass(get_class($this));
	  $class_props = $refl->getProperties(ReflectionProperty::IS_PUBLIC);
	  foreach ($class_props as $prop)
	  {
	    $pn = $prop->name;
	    $this->$pn = null;
	  }
	}

  public function __wakeup()
  {
    if($this->__conn instanceof db && $this->__conn->getConnName())
    {
      $db = db::getInstance();
      $this->__conn = $db->getSecConnection($this->__conn->getConnName());
    }  else
    {
      $this->__conn = db::getInstance();
    }
  }

  public function getSQL($operation)
  {
    return $this->__SQL[$operation];
  }

}
